package org.catpay.common.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.catpay.common.annotation.Ignore;
import org.catpay.common.annotation.UpdateIgnore;
import org.springframework.stereotype.Component;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;

@Aspect
@Component
public class UpdateIgnoreAspect {
	

	@Around("@annotation(org.catpay.common.annotation.UpdateIgnore)")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		
		
		MethodSignature signature = (MethodSignature) point.getSignature();
		Method method = signature.getMethod();
		
		List<Map<Integer,Class<?>[]>> list = getValidateParameter(method);
		Object[] obj = point.getArgs();
		
		for(Map<Integer,Class<?>[]> map :list) {
			for(Integer key : map.keySet()){
				if(ArrayUtil.isNotEmpty(obj)) {
					Field[] fields = ReflectUtil.getFields(obj[key].getClass());
					List<Field> hasAnnotionFields = new ArrayList<>();
					for(Field f: fields) {
						Ignore ignore = f.getAnnotation(Ignore.class);
						if(ignore != null) {
							hasAnnotionFields.add(f);
						}
					}
					if(map.get(key).length == 0) {
						for(Field f: hasAnnotionFields) {
							invokeMethod(obj[key],f);
						}
					}else {
						for(Class<?> clz : map.get(key)) {
							for(Field f: hasAnnotionFields) {
								Ignore ignore = f.getAnnotation(Ignore.class);
								Class<?>[] ignoreGroups = ignore.groups();
								for(Class<?> ignoreGroup:ignoreGroups) {
									if(ignoreGroup == clz) {
										invokeMethod(obj[key],f);
									}
								}
							}
						}
					}
					
				}
			}
		
		}
		return point.proceed();
	}
	
	private List<Map<Integer,Class<?>[]>> getValidateParameter(Method method){
		Annotation[][] annos = method.getParameterAnnotations();
		List<Map<Integer,Class<?>[]>> result = new ArrayList<>();
		for(int i=0;i<annos.length;i++) {
			for(int j=0;j<annos[i].length;j++) {
				if(annos[i][j].annotationType() == UpdateIgnore.class) {
					UpdateIgnore ui = (UpdateIgnore) annos[i][j];
					Map<Integer,Class<?>[]> map = new HashMap<>();
					map.put(i, ui.groups());
					result.add(map);
				}
			}
		}
		return result;
	}
	
	private void invokeMethod(Object obj,Field f) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
		String fieldSetName = parSetName(f.getName());
		if(!f.getType().isPrimitive()) {
			Method m = ReflectUtil.getMethod(obj.getClass(), fieldSetName, f.getType());
			m.invoke(obj,new Object[]{null});
		}
	}
	
	private String parSetName(String fieldName) {
		if(StrUtil.isNotBlank(fieldName)) {
			int startIndex = 0;
			return "set" + fieldName.substring(startIndex, startIndex + 1).toUpperCase()
					+ fieldName.substring(startIndex + 1);

		}
		return null;
	}
}
